﻿using System;
using System.Reflection;
using System.Collections.Generic;

using SBPweb.Forge.Contracts.Components;

namespace SBPweb.Communication.Shared.Library.Commands
{

    public abstract class CommandManagerBase : InitializableBase, ICommandManager
    {

        #region Private fields

        private static Dictionary<Type, ConstructorInfo> dictConstructors = new Dictionary<Type, ConstructorInfo>();
        private static Dictionary<Type, MulticastDelegate> dictHandlers = new Dictionary<Type, MulticastDelegate>();

        #endregion

        #region Protected fields

        protected MessengerBase messenger;

        #endregion

        #region Constructor logic

        protected CommandManagerBase(MessengerBase messenger)
        {
            this.messenger = messenger;
        }

        #endregion

        #region Property accessors

        public ICommandManagerConfiguration Configuration
        {
            get;
            protected set;
        }

        #endregion

        #region Events

        public event EventHandler<CommandBaseCancelEventArgs> CommandSending;

        public event EventHandler<CommandBaseEventArgs> CommandSent;

        #endregion

        #region Methods

        public virtual void SendCommand(ICommand command)
        {
            this.SendCommand((CommandBase)command);
        }

        public abstract void SendCommand(CommandBase command);

        public void ApplyConfiguration(ICommandManagerConfiguration configuration)
        {
            Configuration = configuration;
        }

        #region Typed handlers for commands

        public void RegisterCommandHandler<TCommand>(EventHandler<CommandBaseEventArgs<TCommand>> handler)
            where TCommand : CommandBase
        {
            Type t = typeof(TCommand);
            lock (dictHandlers)
            {
                if (dictHandlers.ContainsKey(t))
                {
                    dictHandlers[t] = (MulticastDelegate)MulticastDelegate.Combine(dictHandlers[t], handler);
                }
                else
                {
                    dictHandlers.Add(t, handler);
                }
            }
        }

        public void UnregisterCommandHandler<TCommand>(EventHandler<CommandBaseEventArgs<TCommand>> handler)
            where TCommand : CommandBase
        {
            Type t = typeof(TCommand);
            lock (dictHandlers)
            {
                if (dictHandlers.ContainsKey(t))
                {
                    if (dictHandlers[t].GetInvocationList().Length > 1)
                    {
                        dictHandlers[t] = (MulticastDelegate)MulticastDelegate.Remove(dictHandlers[t], handler);
                    }
                    else
                    {
                        dictHandlers.Remove(t);
                    }
                }
            }
        }

        protected void OnCommandReceived(object sender, CommandBase command)
        {
            Type t = command.GetType();
            lock (dictHandlers)
            {
                if (dictHandlers.ContainsKey(t))
                {
                    ConstructorInfo cI = null;
                    lock (dictConstructors)
                    {
                        if (dictConstructors.ContainsKey(t))
                        {
                            cI = dictConstructors[t];
                        }
                        else
                        {
                            Type type = typeof(CommandBaseEventArgs).BaseType.GetGenericTypeDefinition().MakeGenericType(t);
                            cI = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { t }, null);
                            dictConstructors.Add(t, cI);
                        }
                    }
                    object o = cI.Invoke(new object[] { command });
                    dictHandlers[t].DynamicInvoke(sender, o);
                }
            }
        }

        #endregion

        #endregion

    }

}